Custom Attributes  >  Garbage Collection  >  Class Helpers

Garbage Collection

The Common Language Runtime is a garbage-collected environment. This means the programmer is freed (for the most part) from worrying about memory allocation. Broadly speaking, after you allocate memory, the CLR determines when it is safe to free that memory. "Safe to free" means that no more references to that memory exist.

Finalization

Every class in the .NET Framework (including Delphi for Microsoftâ .NET classes) inherits a method called Finalize. The garbage collector calls the Finalize method when the memory for the object is about to be freed. There are many potential problems with this arrangement, not the least of which being that you have no control over when the Finalize method is called. The asynchronous nature of finalization is a problem for objects that open resources such as file handles and database connections, because the Finalize method might not be called for some time, leaving these connections open.

 

Another way to free up resources is to implement what is known as the dispose pattern. Classes adhering to the dispose pattern must implement the .NET interface called IDisposable. IDisposable contains only one method, called Dispose. Unlike the Finalize method, the Dispose method is public. It can be called directly by a user of the class, as opposed to relying on the garbage collector to call it. This gives you back control of freeing resources, but calling Dispose still does not reclaim memory for the object itself - this is still under the pervue of the garbage collector. Note that some classes in the .NET Framework implement both Dispose, and another method such as Close. Typically the Close method simply calls Dispose, but the extra method is provided because it seems more "natural" for certain classes such as files.

The Dispose Pattern in Delphi for .NET

Delphi for .NET classes are free to use the Finalize method for freeing system resources, however the recommended method is to implement the dispose pattern. The Delphi for .NET compiler makes life even easier, by recognizing a very specific destructor pattern in your class, and silently implementing the IDisposable interface for you. This enables you to continue writing new code the same way you always have, while allowing much of your existing Delphi code to run in the garbage collected world of the CLR.

 

The compiler recognizes the specific pattern of a Delphi destructor:

 

      TMyClass = class(TObject)

            destructor Destroy; override;

      end;

 

Your destructor must fit this pattern exactly:

 

When the compiler recognizes a destructor that fits the pattern, it silently implements the IDisposable interface for you. The Free method is written so that if the class implements the IDisposable interface (which it does), then the Dispose method is called, which in turn calls your destructor.

 

You can still implement the IDisposable interface directly, if you choose. However, the compiler's automatic implementation of the Free-Dispose-Destroy mechanism cannot coexist with your implementation of IDisposable. The two methods are mutually exclusive. You must choose to either implement IDisposable directly, or equip your class with the familiar destructor Destroy; override pattern and rely on the compiler to do the rest. The Free method will call Dispose in either case, but if you implement Dispose yourself, you must call your destructor yourself. If you want to implement IDisposable yourself, your destructor cannot be called Destroy.

 

Reference-counted Objects in Delphi for .NET

Reference counting, as a technique for memory management, is deprecated in the .NET Framework. The CLR handles all memory allocation for you. In this preview release of the Delphi for .NET compiler, the TInterfacedObject class is an alias for TObject. The AddRef, Release, and QueryInterface methods do not exist.

 

If you have existing Delphi code that relies on reference counting to call your destructor, you will have to reimplement this code when porting to the .NET environment. The reason for this is that external consumers of your component (written in languages other than Delphi) will not be calling AddRef and Release like they used to. Instead, they will be relying on the garbage collector. The recommended approach is to use the destructor Destroy; override pattern described above.

Unit Initialization and Finalization

Unit initialization and finalization continue to work in Delphi for .NET programs, though there are some migration issues to be aware of.

 

Units that you depend on will be initialized prior to initializing your own unit. However, there is no way to guarantee the order in which units are initialized, nor is there a way to guarantee when they will be initialized. Be aware of initialization code that depends on another unit's initialization side effects, such as the creation of a file. Such a dependency cannot be made to work reliably under .NET.

 

Unit finalization continues to exist as a language feature, but it is subject to the same constraints and difficulties as the Finalize method of objects. These are, that unit finalization is asynchronous, and, there no way to determine when it will happen.

 

The main points to keep in mind for garbage collection, and unit initialization and finalization are:

 


Top  |  Contents  |  Copyrights  |  Borland Developer Network  |  Borland Home